/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package WorkshopServerSide;

import LoggerServerSide.Clock;
import java.util.Random;
import java.util.TreeMap;
import sd_proj1.interfaces.*;

/**
 * Esta classe WorkshopMonitor cria um objecto do tipo Oficina onde vão interagir a dona e os artesãos.
 *
 * @author Pedro Costa Correia - 32795
 * @author Valter Pedro Lopes Vicente - 39360
 */
public class WorkshopMonitor implements IOwnerWorkshop, ICraftsman {

	/**
	 *  Este tipo de dados implementa a Oficina - é aqui que tem lugar toda a comunicação.
	 *  Solução concorrente baseada em monitores como elemento de sincronização entre os threads Craftsman e Owner.
	 */
	/**
	 *  Quantidade de vezes que a dona repõem matérias primas.
	 *
	 *    @serialField couldSupplyMaterials
	 */
	private final int couldSupplyMaterials = 3;
	/**
	 *  Número de artesãos na oficina
	 *
	 *    @serialField nCraftsman
	 */
	private int nCraftsman;
	/**
	 *  Quantidade de matérias primas disponíveis na oficina.
	 *
	 *    @serialField primeMaterialsAmount
	 */
	private int primeMaterialsAmount;
	/**
	 *  Número de vezes que as matérias primas foram disponibilizadas.
	 *
	 *    @serialField timesSupplied
	 */
	private int timesSupplied = 0;
	/**
	 *  Total de matérias primas que já foram disponibilizadas.
	 *
	 *    @serialField totalPrimeMaterials
	 */
	private int totalPrimeMaterials = 0;
	/**
	 *  Número de produtos terminados presentes na oficina.
	 *
	 *    @serialField finishedProducts
	 */
	private int finishedProducts = 0;
	/**
	 *  Total de produtos que já foram produzidos.
	 *
	 *    @serialField totalProduced
	 */
	private int totalProduced = 0;
	/**
	 *  Quantidade de matérias primas que servirá de alerta para buscar mais.
	 *
	 *    @serialField alert
	 */
	private int alert;
	/**
	 *  Vector de armazenamento da quantidade de produtos feitos por cada artesão.
	 *
	 *    @serialField craftsmanProduced
	 */
	private int[] craftsmanProduced;
	/**
	 *  Instanciação da interface do artesão implementada no estado geral.
	 *
	 *    @serialField craftsmanState
	 */
	private ICraftsmanFifo craftsmanState;
	/**
	 *  Usado para armazenamento de mensagens.
	 *
	 *    @serialField map
	 */
	private TreeMap map;
	/**
	 *  Uma mensagem para ser usada no log.
	 *
	 *    @serialField mlog
	 */
	private Message mlog;

	/**
	 * Instanciação do monitor da oficina.
	 * @param nCraftsman Número de artesãos na oficina.
	 * @param primeMaterialsAmount Quantidade de matérias primas presentes.
	 * @param alert Alerta de que são necessárias mais matérias primas.
	 * @param craftsmanState Interface do artesão associada ao estado geral.
	 * @param map TreeMap de armazenamento de mensagens.
	 */
	public WorkshopMonitor(int nCraftsman, int primeMaterialsAmount, int alert, ICraftsmanFifo craftsmanState, TreeMap map) {
		this.nCraftsman = nCraftsman;
		this.primeMaterialsAmount = primeMaterialsAmount;
		this.alert = alert;
		this.craftsmanState = craftsmanState;
		this.map = map;

		craftsmanProduced = new int[nCraftsman];
		for (int i = 0; i < craftsmanProduced.length; i++) {
			craftsmanProduced[i] = 0;
		}

		if (primeMaterialsAmount < alert || primeMaterialsAmount == 0) {
			this.craftsmanState.primeMaterialsNeeded(0);
		}
	}

	/**
	 * O artesão morre (originada pelo artesão).
	 * @param craftsmanId ID do artesão.
	 * @return <li> true, se morre.
	 *		<li> false, se não morre.
	 */
	public synchronized boolean endOperCraftsman(int craftsmanId) {
		if (primeMaterialsAmount > 0 && timesSupplied <= 3) {
			return false;
		} else {
			nCraftsman--;
			if (nCraftsman == 0) {
				craftsmanState.batchReadyForTransfer(finishedProducts);
				craftsmanState.setAllCraftsDead(true);

			}
			return true;
		}
	}

	/**
	 * Um artesão vai buscar matérias primas para criar produtos (originada pelo artesão).
	 * @param craftsmanId ID do artesão.
	 * @return <li> true, se a quantidade de matérias primas atingir o limite de alerta.
	 *		<li> false, se ainda está dentro do limite ou caso tenha sido tirado algum inferior ao limite.
	 */
	public synchronized boolean collectMaterials(int craftsmanId) {
		if (primeMaterialsAmount == 0 && timesSupplied < couldSupplyMaterials) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}

		primeMaterialsAmount--;
		mlog = new Message();
		mlog.setstatCraftsman(Message.craftsmanStat.FTPM, craftsmanProduced[craftsmanId], craftsmanId);
		mlog.setWorkShopStat(primeMaterialsAmount, finishedProducts, timesSupplied, totalPrimeMaterials, totalProduced);
		map.put(Clock.getInstance().getValue(), mlog);

		if (primeMaterialsAmount == alert && timesSupplied < couldSupplyMaterials) {
			return true;
		}
		return false;
	}

	/**
	 * O artesão volta ao trabalho, indo buscar mais máteria prima (originada pelo artesão).
	 * @param craftsmanId ID do artesão.
	 */
	public synchronized void backToWork(int craftsmanId) {
		mlog = new Message();
		mlog.setstatCraftsman(Message.craftsmanStat.FTPM, craftsmanProduced[craftsmanId], craftsmanId);
		map.put(Clock.getInstance().getValue(), mlog);
	}

	/**
	 * O artesão está pronto a produzir um novo produto (originada pelo artesão).
	 * @param craftsmanId ID do artesão.
	 */
	public synchronized void prepareToProduce(int craftsmanId) {
		mlog = new Message();
		mlog.setstatCraftsman(Message.craftsmanStat.PANP, craftsmanProduced[craftsmanId], craftsmanId);
		map.put(Clock.getInstance().getValue(), mlog);
	}

	/**
	 * O artesão coloca um produto acabado na estante da oficina (originada pelo artesão).
	 * @param craftsmanId ID do artesão.
	 * @return A quantidade de produtos acabados presentes na oficina.
	 */
	public synchronized int goToStore(int craftsmanId) {
		finishedProducts++;
		totalProduced++;

		mlog = new Message();
		mlog.setstatCraftsman(Message.craftsmanStat.SIFT, ++craftsmanProduced[craftsmanId], craftsmanId);
		mlog.setWorkShopStat(primeMaterialsAmount, finishedProducts, timesSupplied, totalPrimeMaterials, totalProduced);
		map.put(Clock.getInstance().getValue(), mlog);

		return finishedProducts;
	}

	/**
	 * A dona traz matérias primas para oficina (originada pela dona).
	 */
	public synchronized void visitSuppliers() {
		if (timesSupplied < couldSupplyMaterials) {
			int newMaterials = ((new Random()).nextInt(100) + 1);
			primeMaterialsAmount += newMaterials;
			timesSupplied++;
			totalPrimeMaterials += newMaterials;

			mlog = new Message();
			mlog.setOwnerStat(Message.ownerStat.DLPM);
			mlog.setShopDoorStat(Message.storeStat.SPCL);
			mlog.setShopStat(-1, -1, '0', 'F');
			mlog.setWorkShopStat(primeMaterialsAmount, finishedProducts, timesSupplied, totalPrimeMaterials, totalProduced);
			map.put(Clock.getInstance().getValue(), mlog);
		}

		notifyAll();
	}

	/**
	 * A dona vem à oficina e leva todos os produtos terminados (originada pela dona).
	 */
	public synchronized void goToWorkshop() {
		craftsmanState.setNProductsTransport(finishedProducts);
		finishedProducts = 0;

		mlog = new Message();
		mlog.setOwnerStat(Message.ownerStat.CBOP);
		mlog.setShopDoorStat(Message.storeStat.SPCL);
		mlog.setShopStat(-1, -1, 'F', '0');
		mlog.setWorkShopStat(primeMaterialsAmount, finishedProducts, timesSupplied, totalPrimeMaterials, totalProduced);
		map.put(Clock.getInstance().getValue(), mlog);
	}
}
